package com.example.synatx.stream;

import org.junit.Test;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @auth guoxh
 * @date 2020/3/2-下午6:31
 * @desc stream API 声明式;可复用;可并行.
 *
 * Stream与集合的区别
 * 1. 集合必须全部加载到内存中才能执行;流可以一边执行一遍取数;
 * 2. 流只能加载一次
 * 3. 流使用的内部迭代,集合使用的是外部迭代.外部迭代的执行需要自己控制
 *
 * 筛选,切片,查找,匹配,映射和规约.
 **/
public class StreamAPITest {

    /**
     * filter,sorted,map limit等操作返回的都是流,再调用collect操作之前,没有任何结果产生,实际上根本就没有从menu中选择元素.
     * 链中的方法调用都在排队等待,直到调用collect
     */
    public void test01(){
        List<Dish> menu = new ArrayList<>();
        List<String> names = menu.stream().filter(dish -> dish.getCalories()<400).sorted(Comparator.comparing(Dish::getCalories)).map(Dish::getName).collect(Collectors.toList());

        List<String> names1 = menu.parallelStream().filter(dish -> dish.getCalories()<400).sorted(Comparator.comparing(Dish::getCalories)).map(Dish::getName).collect(Collectors.toList());
    }

    public void test02(){
        List<String> list = Arrays.asList("1","b","c");

    }

    public void test03(){
        List<Integer> list = Arrays.asList(1,3,5,7,2,3,5,1);
        //distinct
        list.stream().distinct().forEach(System.out::println);
        //limit
        list.stream().filter( i -> i%2==0).limit(3).forEach(System.out::println);
        //跳过元素
        list.stream().filter(i -> i%2 ==0).skip(1).forEach(System.out::println);
    }

    /**
     * map
     */
    public void test04(){
        List<Dish> list = new ArrayList<>();
        list.stream().map(Dish::getCalories).collect(Collectors.toList());
        list.stream().map(Dish::getName).map(String::length).collect(Collectors.toList());

    }

    /**
     * 返回每个数值的平方根
     */
    public void test05(){
        List<Integer> list = Arrays.asList(1,2,3,4);
        list.stream().map(i -> i*i).collect(Collectors.toList());
    }

    /**
     * 返回两个数组组成的数对
     */
    public void test06(){
        List<Integer> number1 = Arrays.asList(1,2,3);
        List<Integer> number2 = Arrays.asList(3,4);

        number1.stream().flatMap(i -> number2.stream().map(j-> new int[]{i,j})).collect(Collectors.toList());
    }

    /**
     * 返回两个数组组成的数对且数对之和能被3整除
     */
    public void test07(){
        List<Integer> number1 = Arrays.asList(1,2,3);
        List<Integer> number2 = Arrays.asList(3,4);

        number1.stream().flatMap(i->number2.stream().filter(j-> (i+j)%3==0).map(j->new int[]{i,j})).collect(Collectors.toList());
    }

    /**
     * 匹配
     * anyMatch,allMatch,noneMatch
     */
    public void test08(){
        List<Dish> menu = new ArrayList<>();
        menu.stream().anyMatch(Dish::isVegetarian);
        menu.stream().allMatch(dish -> dish.getCalories()>1000);
        menu.stream().noneMatch(dish -> dish.isVegetarian());
    }

    /**
     * findFirst findAny
     */
    public void test09(){

    }

    public void test10(){
        List<Integer> list = Arrays.asList(1,3,5,2,6);
        int sum1 = list.stream().reduce(0,(a,b) -> a+b);

        Optional<Integer> sum2 = list.stream().reduce((a,b) -> a+b);

        Optional<Integer> max = list.stream().reduce(Integer::max);

        Optional<Integer> min = list.stream().reduce(Integer::min);


    }

    /**
     * IntStream, DoubleStream,LongStream
     * OptionalInt
     * 数值范围 range rangeClosed
     * 统计1到100的偶数
     */
    public void test11(){
        IntStream intStream = IntStream.range(1,100).filter(i ->i%2 == 0);
        System.out.println(intStream.count());

    }

    /**
     * 生成勾股定理数字
     */
    public void  test12(){
        Stream<int[]> streams = IntStream.rangeClosed(1,100).boxed().flatMap(a ->
            IntStream.rangeClosed(a,100).filter(b -> Math.sqrt(a*a+b*b) % 1 == 0).boxed().map(b -> new int[]{a,b, (int) Math.sqrt(a*a+b*b)})
        );

        Stream<double[]> stream = IntStream.rangeClosed(1,100).boxed().flatMap( a ->
                IntStream.rangeClosed(a,100).boxed().map(b -> new double[]{a,b,Math.sqrt(a*a+b*b)})
                .filter(t->t[2]%1==0)
        );
    }

    /**
     * Stream.of() 静态方法,通过显式值创建一个流,他可以接受任意数量的参数
     * Arrays.stream 从数组创建流,它接受一个数组作为参数
     */
    public void test13(){
        Stream<String> stringStream = Stream.of("java8","Lambda","IN","Action");
        stringStream.map(String::toUpperCase).forEach(System.out::println);

        int[] arr = {1,2,3,4,5,6};
        Arrays.stream(arr).sum();
    }

    /**
     * 文件生成流
     * java.nio.file.Files中有很多静态方法都会返回一个流.
     * Files.lines它会返回一个由指定文件中的各行构成的字符串流.
     */
    public void test14() throws IOException {
        long uniqueWord = 0;
        try( Stream<String> line = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
           uniqueWord = line.flatMap(lines -> Arrays.stream(lines.split(","))).distinct().count();
        }
    }

    /**
     * 无限流
     * Stream API 提供两个方法来从函数生成流. Stream.iterate Stream.generate
     * 由iterate generate 产生的流会按照给定的函数按需创建值,所以可以无限计算下去.
     * 所以一般需要通过 limit(n)来进行限制
     */
    public void test15(){
        Stream.iterate(0,n->n+2).limit(10).forEach(System.out::print);
    }


    /**
     * 斐波那契数列
     */
    @Test
    public void test16(){
        Stream.iterate(new int[]{0,1},t->new int[]{t[1],t[0]+t[1]}).limit(10).map(t -> t[0]).forEach(System.out::println);
    }

    public void test17(){
        Stream.generate(Math::random).limit(5).forEach(System.out::println);
    }


    public static void main(String[] args) {

        List<Integer> list = Arrays.asList(1,3,5,2,6);

        list.sort(Comparator.comparing(Integer::byteValue));

        List<String> strList = Arrays.asList("Hello","String");
        //通过map 返回的是 String数组
        List<String[]> words = strList.stream().map(s -> s.split("")).collect(Collectors.toList());
        //扁平流 floatMap 把每个流中的每个值都换成另一个流,然后把所有的流连接成一个流
        List<String> word = strList.stream().map(s -> s.split("")).flatMap(Arrays::stream).collect(Collectors.toList());
        System.out.println(word);

        int sum = list.stream().reduce(0,(a,b) -> a+b);
        System.out.println(sum);
    }
}
